package com.haoshuang.zhouzhoubang.utils

import com.alibaba.sdk.android.oss.ClientConfiguration
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSS
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.common.OSSLog
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider
import com.alibaba.sdk.android.oss.model.ObjectMetadata
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import com.haoshuang.zhouzhoubang.MyApp
import com.haoshuang.zhouzhoubang.config.Config
import com.mufeng.libs.utils.ktx.runOnUIThread
import java.io.File
import java.net.URLConnection
import kotlin.concurrent.thread
import kotlin.random.Random

object OSSUploadUtils {
    private var oss: OSS? = null

    init {
        if (null == oss) {
            val credentialProvider: OSSCredentialProvider =
                OSSPlainTextAKSKCredentialProvider(Config.accessKeyId, Config.accessKeySecret)
            val conf = ClientConfiguration()
            conf.connectionTimeout = 150 * 1000 // 连接超时，默认15秒
            conf.socketTimeout = 150 * 1000 // socket超时，默认15秒
            conf.maxConcurrentRequest = 5 // 最大并发请求书，默认5个
            conf.maxErrorRetry = 2 // 失败后最大重试次数，默认2次
            OSSLog.enableLog()
            oss = OSSClient(MyApp.instance.applicationContext, Config.endpoint, credentialProvider, conf)
        }
    }

    /**
     * 上传单图
     * @param uploadObject String
     * @param path String
     * @param block [@kotlin.ExtensionFunctionType] Function1<UploadAction<String>, Unit>
     */
    fun uploadFile(
        uploadObject: String,
        path: String,
        block: UploadAction.() -> Unit
    ) {
        val action = UploadAction().apply(block)
        thread {
            action.start?.invoke()
            // 拼接路径
            val key = buildPath(uploadObject, path)

            var metadata: ObjectMetadata? = null
            val mimeType = getMimeType(File(path))
            if (mimeType.startsWith("video")) {
                metadata = ObjectMetadata()
                metadata.contentType = "video/mp4"
            }

            // 构造上传请求
            val put = PutObjectRequest(Config.testBucket, key, path)
            if (metadata != null) {
                put.metadata = metadata
            }
            // 异步上传设置进度回调
            put.setProgressCallback { request, currentSize, totalSize ->
                runOnUIThread {
                    action.progress?.invoke(currentSize, totalSize)
                }
            }

            oss?.asyncPutObject(
                put,
                object : OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
                    override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
                        runOnUIThread {
                            val url = if (!mimeType.startsWith("video")) {
                                oss?.presignPublicObjectURL(Config.testBucket, key)
                            } else {
                                oss?.presignPublicObjectURL(Config.testBucket, key)
                            }
                            action.success?.invoke(url)
                        }
                    }

                    override fun onFailure(
                        request: PutObjectRequest?,
                        clientException: ClientException?,
                        serviceException: ServiceException?
                    ) {
                        runOnUIThread {
                            action.error?.invoke(clientException, serviceException)
                        }
                    }

                })

        }
    }

    fun uploadFiles(
        uploadObject: String,
        paths: MutableList<String>,
        block: UploadAction.() -> Unit
    ) {
        val action = UploadAction().apply(block)
        val urlMap = mutableMapOf<Int, String>()
        thread {
            runOnUIThread { action.start?.invoke() }
            for ((index, path) in paths.withIndex()) {
                uploadFile(uploadObject, path){
                    progress { currentSize, totalSize ->
                        runOnUIThread {
                            action.progressItem?.invoke(currentSize, totalSize, index)
                        }
                    }
                    success {
                        runOnUIThread {
                            val url = it
                            urlMap[index] = url?:""
                            if (urlMap.size == paths.size){
                                urlMap.toSortedMap()
                                action.successAll?.invoke(urlMap.values.toMutableList())
                            }else{
                                action.successItem?.invoke(it, index)
                            }
                        }
                    }
                    error{clientException, serviceException ->
                        runOnUIThread {
                            action.error?.invoke(clientException, serviceException)
                        }
                    }
                }
            }
        }
    }

    private fun buildPath(uploadObject: String, path: String): String {
        // 命名方式 日期+随机数
        val currentTime = (System.currentTimeMillis() / 1000).toString() + getNumLargeSmallLetter(5)
        val suffixName = path.substring(path.lastIndexOf('.'), path.length)
        return "${uploadObject}/$currentTime$suffixName"
    }

    /**
     * 数字和大小写字母混编字符串
     * @param size Int
     */
    private fun getNumLargeSmallLetter(size: Int): String {
        val buffer = StringBuffer()
        for (i in 0..size) {
            if (Random.nextInt(2) % 2 == 0) {
                if (Random.nextInt(2) % 2 == 0) {
                    buffer.append((Random.nextInt(27) + ('A' as Char).code).toChar())
                } else {
                    buffer.append((Random.nextInt(27) + 'a'.code).toChar())
                }
            } else {
                buffer.append(Random.nextInt(10))
            }
        }
        return buffer.toString()
    }

    private fun getMimeType(file: File): String {
        val fileNameMap = URLConnection.getFileNameMap()
        return fileNameMap.getContentTypeFor(file.name)
    }

}

class UploadAction {
    // 开始请求
    var start: (() -> Unit)? = null
        private set

    var progress: ((currentSize: Long, totalSize: Long) -> Unit)? = null
        private set

    var progressItem: ((currentSize: Long, totalSize: Long, position: Int) -> Unit)? = null
        private set

    // 请求成功
    var success: ((url: String?) -> Unit)? = null
        private set

    var successItem: ((url: String?, position: Int) -> Unit)? = null
        private set

    var successAll: ((urlList: MutableList<String>?) -> Unit)? = null
        private set

    // 请求失败
    var error: ((clientException: ClientException?, serviceException: ServiceException?) -> Unit)? =
        null
        private set

    fun start(block: () -> Unit) {
        start = block
    }

    fun progress(block: (currentSize: Long, totalSize: Long) -> Unit) {
        progress = block
    }

    fun progressItem(block: (currentSize: Long, totalSize: Long, position: Int) -> Unit){
        progressItem = block
    }

    fun success(block: (String?) -> Unit) {
        success = block
    }

    fun successItem(block: (String?, Int) -> Unit) {
        successItem = block
    }

    fun successAll(block: (MutableList<String>?) -> Unit) {
        successAll = block
    }

    fun error(block: (clientException: ClientException?, serviceException: ServiceException?) -> Unit) {
        error = block
    }

}

